package express.bttree;

import express.LogMgr;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;



/**
 * 作者：陈宏鸿
 * 功能：叶子节点
 */
@Data
public abstract class OpNode extends Node{
    private String method;

    private List<Object> params;

    private Object value;
    private Operator op;

    public OpNode() {

    }

    public void setMethod(String method) {
        this.method = method;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public void setOp(Operator op) {
        this.op = op;
    }

    private void printLog(String methodName, Object result, Boolean ok) {
        String logText = "exp=" +methodName + "("+ this.params +") " + this.op + " " + this.value + ";";
        logText += "result=" + result + ";";
        logText += "ok=" + ok + ";";
        LogMgr.info(logText);
    }

    protected abstract Map<String, BiFunction<Object,List<Object>,Object>> getMethods();

    @Override
    public State execute(Object target) {
        if(this.method == null || this.value == null || this.op == null) {
            throw new RuntimeException("必要参数未设置，请检查");
        }

        BiFunction<Object,List<Object>,Object> methodFunc = getMethods().get(this.method);
        if(methodFunc == null) {
            throw new RuntimeException(this.method + " 未定义");
        }
        Object result = methodFunc.apply(target, this.params);
        if(!Utils.comparable(result, this.value)) {
            throw new RuntimeException("表达式 "+ this.method +" 不可比较，result:" + result + ",value:" + this.value);
        }
        if(result instanceof Boolean) {
            boolean ok = (boolean) result == Boolean.parseBoolean(this.value.toString());
            printLog(this.method, result, ok);
            if(ok) {
                return State.True;
            } else {
                return State.False;
            }
        } else if(result instanceof Integer || result instanceof Long) {
            long exp1 = Long.parseLong(result.toString());
            long exp2 = Long.parseLong(this.value.toString());
            boolean ok;
            switch (this.op) {
                case eq:
                    ok = exp1 == exp2;
                    break;
                case gt:
                    ok = exp1 > exp2;
                    break;
                case gte:
                    ok = exp1 >= exp2;
                    break;
                case lt:
                    ok = exp1 < exp2;
                    break;
                case lte :
                    ok = exp1 <= exp2;
                    break;
                case ne:
                    ok = exp1 != exp2;
                    break;
                default:
                    throw new RuntimeException("运算符不存在");
            }
            printLog(this.method, result, ok);
            if(ok) {
                return State.True;
            } else {
                return State.False;
            }
        } else if(result instanceof String){
            if(result == null) {
                return State.Break;
            }
            String exp1 = result.toString();
            String exp2 = this.value.toString();
            boolean ok;
            switch (this.op) {
                case eq:
                    ok = StringUtils.equals(exp1, exp2);
                    break;
                case ne:
                    ok = !StringUtils.equals(exp1, exp2);
                    break;
                default:
                    LogMgr.error("字符串不支持该运算符，op:"+this.op);
                    return State.Break;
            }
            if(ok) {
                return State.True;
            } else {
                return State.False;
            }
        } else {
            LogMgr.error("该类型不支持，result:"+ result);
            return State.Break;
        }
    }

    @Override
    public String toString() {
        return "OpNode{" +
                "method='" + method + '\'' +
                ", value=" + value +
                ", op=" + op +
                '}';
    }
}
